/**
 * 
 * 
 *  
 *====================================================
 * 文件名称: ActionFilter.java
 * 修订记录：
 * No    日期				作者(操作:具体内容)
 * 1.    Mar 3, 2009		(创建:创建文件)
 * 2.    Mar 3, 2009        根据 注释 规范进行调整
 *====================================================
 * 类描述：
 * 
 */
package com.devframe.web.filter;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.devframe.exception.MyException;
import com.devframe.sys.Constants;
import com.devframe.sysbase.model.TSysLogon;
import com.devframe.sysbase.model.TSysOperatorLog;
import com.devframe.sysconfig.service.SysRightService;
import com.devframe.web.loader.MyWebContextLoader;
import com.devframe.web.util.jexl.JexlServletContext;
import com.devframe.web.util.user.UserInfo;
import com.devframe.web.util.user.UserInfoHolder;


/**
 * 
 * <pre>
 * &lt;b&gt;&lt;font color=&quot;blue&quot;&gt;ActionFilter&lt;/font&gt;&lt;/b&gt;
 * </pre>
 * 
 * <pre>
 * &lt;b&gt; --系统功能访问过滤器--&lt;/b&gt;
 * </pre>
 * <pre>
 *   此过滤器必须与登录过滤器配合使用，即本过滤器假设url已经通过了登录过滤器的过滤。
 *   如果是无需过滤的url，请在web.xml中配置noFilter。noFilter中配置的url将不进行权限控制和记录。多个url用逗号分隔
 *   如果url中需要包含特定参数进行控制则可以配置extraParam，多个参数名用逗号分隔
 *   在功能表（视图）中未注册的Url不进行权限控制，但记录操作日志
 * &lt;pre&gt;
 * &lt;b&gt;--样例--&lt;/b&gt;
 *   ActionFilter obj = new ActionFilter();
 *   obj.method();
 * &lt;/pre&gt;
 * JDK版本：JDK1.4
 * &#064;author  &lt;b&gt;&lt;/b&gt;
 * 
 */
public class ActionFilter extends HttpServlet implements Filter {
    /**
     * MENU_INFO_KEY
     */
    public static final String MENU_INFO_KEY = "zhanbs/menuInfoKey";

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 1L;

    /**
     * logger
     */
    static Logger logger = Logger.getLogger(ActionFilter.class);

    /**
     * noFilterUrls 无需过滤的url数组，在web.xml中配置
     */
    private String[] noFilterUrls;

    /**
     * 附加参数，默认method 完整url控制按请求路径，并按顺序加上附加参数组成。
     */
    private String[] extraParams;
    /**
     * 模块特定参数定义
     */
    private Map<String,String[]> moduleExtraParams=new HashMap<String, String[]>();

    /**
     * sysRightService
     */
    private SysRightService sysRightService;
    
    private JexlEngine jexlEngine = new JexlEngine(null, null, null, null);

    /**
     * init 过滤器初始化 根据过滤器参数noFilter配置无需过滤的url
     * 
     * @param filterConfig
     *            FilterConfig
     * @throws ServletException
     *             ServletException
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        jexlEngine.setSilent(true);
        jexlEngine.setLenient(false);
        jexlEngine.setDebug(false);
        // 解析不过滤的url设定
        String noFilter = filterConfig.getInitParameter("noFilter");
        if (noFilter != null) {
            noFilterUrls = noFilter.split(",");
            for (int i = 0; i < noFilterUrls.length; i++) {
                noFilterUrls[i] = noFilterUrls[i];
            }
        }
        // 解析额外参数设定，默认method
        String extraParam = filterConfig.getInitParameter("extraParam");
        if (extraParam != null) {
            extraParams = extraParam.split("[,; ]");
        }
        if (extraParams == null) {
            extraParams = new String[] { "method" };
        }
        //解析特定URL需要读取其他参数的配置
        Enumeration enu = filterConfig.getInitParameterNames();
        while (enu.hasMoreElements()){
            String pName = (String) enu.nextElement();
            String pValue = (String) filterConfig.getInitParameter(pName);
            if(pName.startsWith("extraParam:")){
                String module = pName.substring("extraParam:".length());
                moduleExtraParams.put(module, pValue.split("[,]"));
            }
        }

    }

    /**
     * 判断是否不过滤
     * 
     * 如果请求url包含无需过滤的url设置的表达式，则不过滤。
     * 
     * @param url
     *            url
     * @return 是否属于无需过滤的url
     * 
     */
    private boolean isNofilter(String url) {
        // url=url.toUpperCase();
        if (noFilterUrls != null) {
            for (int i = 0; i < noFilterUrls.length; i++) {
                if (url.endsWith(noFilterUrls[i])) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 过滤器功能实现 无须过滤的url不过滤，需要过滤的url首先判断是否登录 已登录时，判断是否有权限 允许操作时，记录系统操作日志
     * 
     * @param request
     *            ServletRequest
     * @param response
     *            ServletResponse
     * @param filterChain
     *            FilterChain
     * @throws IOException
     *             IOException
     * @throws ServletException
     *             ServletException
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {
    	
        long beginTime = System.currentTimeMillis();
        HttpServletRequest re = (HttpServletRequest) request;
		String uri = re.getRequestURI();  
		if(StringUtils.isNotBlank(uri)){
			uri=uri.substring(1);
		}
		if(StringUtils.isNotBlank(uri)&& uri.toLowerCase().contains("api")){
			filterChain.doFilter(request, response);
		}else{
        //logger.info("ActionFilter开始拦截 context path:"+re.getSession().getServletContext().getRealPath("/"));
	        Map paramsMap=re.getParameterMap();
	        Iterator iter=paramsMap.entrySet().iterator();
	        while(iter.hasNext()){
	        	Map.Entry entry=(Map.Entry)iter.next();
	        	//logger.info("key:"+entry.getKey().toString()+" value:"+((String[])entry.getValue())[0]);
	        }
	        String module = getOperateUrl(re);
	        //logger.info("getOperateUrl:"+module);
	        // 并上附加参数,只处理单个参数，批量提交参数不做处理。
	        String params = getUrlExtraParams(module,re);
	        // 判断是否属于不过滤的范围
	
	        String url= module + params;
	        //logger.info("操作url:" + url+",sessionId:"+re.getSession().getId());
	        UserInfo loginUser = (UserInfo) re.getSession().getAttribute(Constants.USERINFO);
	        //将用户信息存入线程变量，方便程序使用。
	        UserInfoHolder.setUserInfo(loginUser);
	        if (isNofilter(module) || isNofilter(module + params)) {
	            //logger.info("无须过滤url:"+module);
	            filterChain.doFilter(request, response);
	            return;
	        }
	        logger.info("操作url:"+module + params);
	        
	        if (loginUser == null) {
	        	//linzy	
	            logger.info("用户未登陆，不允许操作。");
	            ((HttpServletResponse) response).sendRedirect(re.getContextPath() + "/login.jsp");
	            return;
	        }
	        try {
	            // 获取url对应功能权限信息
	            Map menuInfo = getSysRightService().getUrlMenu(url);
	            //如果URL请求没有注册，则智能判断为相同.do的菜单
	            if(menuInfo==null){
	                menuInfo = getSysRightService().getUrlMenu(module);
	            }
	
	            if (menuInfo == null) {// 未登记，不控制权限
	                menuInfo=new HashMap();
	                menuInfo.put("URL", url);
	                menuInfo.put("MENU_NAME", url);
	                UserInfoHolder.setModuleInfo(menuInfo);
	                // 记录未登记的URL，只在第一次记录
	               request.setAttribute(MENU_INFO_KEY, menuInfo);
	                getSysRightService().createUnKnownUrl(url);
	                filterChain.doFilter(request, response);
	            } else {
	                UserInfoHolder.setModuleInfo(menuInfo);
	                request.setAttribute(MENU_INFO_KEY, menuInfo);
	                Object obj=menuInfo.get("NEED_LOG");
	                String needLog=null;
	                if(obj!=null){
	                	needLog=obj.toString();
	                }
	                String menuCode = (String) menuInfo.get("RIGHT_CODE");
	                String menuName = (String) menuInfo.get("MENU_NAME");
	               obj= menuInfo.get("IS_AJAX");
	               String isAjax =null;
	               if(obj!=null){
	            	   isAjax=obj.toString();
	               }
	                String operInfoTmpl = (String) menuInfo.get("OPER_INFO_TMPL");
	                // 获得该用户的权限ID列表
	                Set userPowers = loginUser.getPowers();
	
	                if (!userPowers.contains(menuCode) && !loginUser.getIsManager()) {
	                    logger.info("用户没有权限:" + url);
	                    if (isAjax != null && isAjax.equals("0")) {// 不是Ajax请求
	                        ((HttpServletResponse) response).sendRedirect(re.getContextPath() + "/common/norule.jsp");
	                    } else {
	                        writeNoRoleAjax(response);
	                    }
	                    return;
	                } else {// 有权限
	                    try {
	                        filterChain.doFilter(request, response);
	                    } catch (RuntimeException e) {
	                        e.printStackTrace();
	                        writeExceptionAjax(response, e);
	                    }
	                    // 判断是否需要记录日志
	                    if (needLog != null && needLog.equals("1")) {
	                        TSysLogon log = (TSysLogon) re.getSession().getAttribute("logger");
	                        String logInfo = getLogInfo(re,operInfoTmpl);
	                        logger.info(logInfo);
	                        createOperatorLog(beginTime, url, loginUser, menuCode, menuName,logInfo, log);
	                    }
	                }
	            }
	            //logger.info("ActionFilter正常拦截结束....");
	        } catch (Exception ex) {
	            ex.printStackTrace();
	            logger.error("ACTION 过滤出错");
	            MyException de = new MyException(ex);
	            re.getSession().setAttribute(MyException.GLOBAL_EXCEPTION_KEY, de);
	            ((HttpServletResponse) response).sendRedirect(re.getContextPath() + "/common/exception.jsp");
	        }
		}
    }

    /**
     * writeExceptionAjax
     * 
     * @param response
     *            ServletResponse
     * @param e
     *            Exception
     * 
     * @throws IOException
     *             IOException
     */
    private void writeExceptionAjax(ServletResponse response, Exception e) throws IOException {
        Map retrunMap = new HashMap();
        retrunMap.put("status", "error");
        retrunMap.put("message", "应用发生错误:" + e.getLocalizedMessage());
        retrunMap.put("detail", e.getStackTrace());
        JSONObject json = JSONObject.fromObject(retrunMap);
        // response.setCharacterEncoding("utf-8"); 在servlet2.3下不能使用
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print(json.toString());
    }

    /**
     * getLogInfo
     * 根据配置的表达式获取操作日志信息
     * @param request HttpServletRequest
     * @param tmpl 日志信息模板
     * @return 日志信息
     * 
     */
    private String getLogInfo(HttpServletRequest request,String tmpl){
        if(tmpl==null){
            return "";
        }
        Object result=null;
        try {
            JexlContext context= new JexlServletContext(request);
            Expression expression = jexlEngine.createExpression(tmpl);
            result = expression.evaluate(context);
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error("操作日志信息模板有错："+tmpl);
        }
        return result==null?"":result.toString();
    }
    /**
     * writeNoRoleAjax
     * 
     * @param response
     *            ServletResponse
     * @throws IOException
     *             IOException
     * 
     */
    private void writeNoRoleAjax(ServletResponse response) throws IOException {
        Map retrunMap = new HashMap();
        retrunMap.put("status", "noRule");
        retrunMap.put("message", "没有权限!!!");
        JSONObject json = JSONObject.fromObject(retrunMap);
        // response.setCharacterEncoding("utf-8"); 在servlet2.3下不能使用
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print(json.toString());
    }

    /**
     * getOperateUrl 解析操作URL
     * 
     * @param re
     *            http请求
     * @return url
     * 
     */
    private String getOperateUrl(HttpServletRequest re) {
        String urlPrefix = "http://" + re.getServerName();
        if (re.getServerPort() != 80) {
            urlPrefix += ":" + re.getServerPort();
        }
        urlPrefix += re.getContextPath() + "/";

        String url = re.getRequestURL().toString();
        url = url.replaceFirst(urlPrefix, "");
        return url;
    }

    /**
     * createOperatorLog
     * 
     * @param beginTime
     *            开始时间
     * @param url
     *            操作URL
     * @param loginUser
     *            登录用户
     * @param menuCode
     *            功能编号
     * @param menuName
     *            功能名称
     * @param log
     *            登录日志ID
     * 
     */
    private void createOperatorLog(long beginTime, String url, UserInfo loginUser, String menuCode,
        String menuName,String operInfo, TSysLogon log) {
        try {
            TSysOperatorLog opLog = new TSysOperatorLog();
            opLog.setActionUrl(url);
            opLog.setFuncCode(menuCode);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            opLog.setOperTime(sdf.format(new Date()));
            if (log != null) {
                opLog.setLogon(log);
            }
            opLog.setOperInfo(operInfo);
            opLog.setUserId(loginUser.getUserId());
            opLog.setUserName(loginUser.getUserName());
            opLog.setFuncName(menuName);
            long timeCost = System.currentTimeMillis() - beginTime;
            opLog.setTimeCost(new Long(timeCost));
            getSysRightService().saveSysOperatorLog(opLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析请求中用于判断权限的附加参数 getUrlExtraParams
     * 
     * @param re
     *            请求
     * @return 参数拼成的url
     * 
     */
    private String getUrlExtraParams(String module,HttpServletRequest re) {
        StringBuffer paramStr = new StringBuffer();
        //读取通用参数
        if (extraParams != null) {
            for (String extraParamName:extraParams) {
                String paramValue = re.getParameter(extraParamName);
                if (paramValue != null) {
                    if (paramStr.length() > 0) {
                        paramStr.append("&");
                    }
                    paramStr.append(extraParamName).append("=").append(paramValue);
                }
            }
        }
        //读取模块特定参数
        String[] moduleParams = moduleExtraParams.get(module);
        if(moduleParams!=null){
            for(String param:moduleParams){
                String pValue = re.getParameter(param);
                if(pValue!=null){
                    if (paramStr.length() > 0) {
                        paramStr.append("&");
                    }
                    paramStr.append(param).append("=").append(pValue);
                }
            }
        }
        
        if (paramStr.length() > 0) {
            paramStr.insert(0, "?");
        }
        return paramStr.toString();
    }

    /**
     * getSysRightService
     * 
     * @return SysRightService
     * 
     */
    private SysRightService getSysRightService() {
        if (sysRightService == null) {
            sysRightService = (SysRightService) MyWebContextLoader.getInstance().getBean("sysRightService");
        }
        return sysRightService;
    }

    /**
     * destroy
     */
    public void destroy() {
    }

}
